/*
 * Copyright (C) 2017 zhouyou(478319399@qq.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.shuyihui.rxhttp.request

import android.content.Context
import android.text.TextUtils
import com.shuyihui.rxhttp.EasyHttp
import com.shuyihui.rxhttp.api.ApiService
import com.shuyihui.rxhttp.cache.RxCache
import com.shuyihui.rxhttp.cache.converter.IDiskConverter
import com.shuyihui.rxhttp.cache.model.CacheMode
import com.shuyihui.rxhttp.https.HttpsUtils
import com.shuyihui.rxhttp.https.HttpsUtils.getSslSocketFactory
import com.shuyihui.rxhttp.interceptor.*
import com.shuyihui.rxhttp.model.HttpHeaders
import com.shuyihui.rxhttp.model.HttpHeaders.Companion.acceptLanguage
import com.shuyihui.rxhttp.model.HttpHeaders.Companion.userAgent
import com.shuyihui.rxhttp.model.HttpParams
import com.shuyihui.rxhttp.utils.HttpLog
import com.shuyihui.rxhttp.utils.RxUtil
import com.shuyihui.rxhttp.utils.Utils
import io.reactivex.Observable
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import retrofit2.CallAdapter
import retrofit2.Converter
import retrofit2.Retrofit
import java.io.File
import java.io.InputStream
import java.net.Proxy
import java.util.*
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier

/**
 *
 * 描述：所有请求的基类
 */
abstract class BaseRequest<out R : BaseRequest<R>>(//请求url
    protected var url: String?
) {
    protected var cache: Cache? = null
    protected var cacheMode = CacheMode.NO_CACHE //默认无缓存
    protected var cacheTime: Long = -1 //缓存时间
    protected var cacheKey //缓存Key
            : String? = null
    protected var diskConverter //设置Rxcache磁盘转换器
            : IDiskConverter? = null
    protected var baseUrl //BaseUrl
            : String?
    protected var readTimeOut //读超时
            : Long = 0
    protected var writeTimeOut //写超时
            : Long = 0
    protected var connectTimeout //链接超时
            : Long = 0
    protected var retryCount //重试次数默认3次
            : Int
    protected var retryDelay //延迟xxms重试
            : Int
    protected var retryIncreaseDelay //叠加延迟
            : Int
    @JvmField
    protected var isSyncRequest //是否是同步请求
            = false
    protected var cookies: MutableList<Cookie?> = ArrayList() //用户手动添加的Cookie
    protected val networkInterceptors: MutableList<Interceptor> = ArrayList()
    protected var headers = HttpHeaders() //添加的header
    var params = HttpParams() //添加的param
        protected set
    @JvmField
    protected var retrofit: Retrofit? = null
    @JvmField
    protected var rxCache //rxcache缓存
            : RxCache? = null
    @JvmField
    protected var apiManager //通用的的api接口
            : ApiService? = null
    protected var okHttpClient: OkHttpClient? = null
    @JvmField
    protected var context: Context
    private var sign = false //是否需要签名
    private var timeStamp = false //是否需要追加时间戳
    private var accessToken = false //是否需要追加token
    protected var httpUrl: HttpUrl? = null
    protected var proxy: Proxy? = null
    protected var sslParams: HttpsUtils.SSLParams? = null
    protected var hostnameVerifier: HostnameVerifier? = null
    protected var converterFactories: MutableList<Converter.Factory> = ArrayList()
    protected var adapterFactories: MutableList<CallAdapter.Factory> = ArrayList()
    protected val interceptors: MutableList<Interceptor> = ArrayList()
    fun readTimeOut(readTimeOut: Long): R {
        this.readTimeOut = readTimeOut
        return this as R
    }

    fun writeTimeOut(writeTimeOut: Long): R {
        this.writeTimeOut = writeTimeOut
        return this as R
    }

    fun connectTimeout(connectTimeout: Long): R {
        this.connectTimeout = connectTimeout
        return this as R
    }

    fun okCache(cache: Cache?): R {
        this.cache = cache
        return this as R
    }

    fun cacheMode(cacheMode: CacheMode): R {
        this.cacheMode = cacheMode
        return this as R
    }

    fun cacheKey(cacheKey: String?): R {
        this.cacheKey = cacheKey
        return this as R
    }

    fun cacheTime(cacheTime: Long): R {
        var cacheTime = cacheTime
        if (cacheTime <= -1) cacheTime = EasyHttp.DEFAULT_CACHE_NEVER_EXPIRE.toLong()
        this.cacheTime = cacheTime
        return this as R
    }

    fun baseUrl(baseUrl: String?): R {
        this.baseUrl = baseUrl
        baseUrl?.let { httpUrl = it.toHttpUrlOrNull() }
//        if (!TextUtils.isEmpty(this.baseUrl)) httpUrl = HttpUrl.parse(baseUrl!!)
        return this as R
    }

    fun retryCount(retryCount: Int): R {
        require(retryCount >= 0) { "retryCount must > 0" }
        this.retryCount = retryCount
        return this as R
    }

    fun retryDelay(retryDelay: Int): R {
        require(retryDelay >= 0) { "retryDelay must > 0" }
        this.retryDelay = retryDelay
        return this as R
    }

    fun retryIncreaseDelay(retryIncreaseDelay: Int): R {
        require(retryIncreaseDelay >= 0) { "retryIncreaseDelay must > 0" }
        this.retryIncreaseDelay = retryIncreaseDelay
        return this as R
    }

    fun addInterceptor(interceptor: Interceptor): R {
        interceptors.add(Utils.checkNotNull(interceptor, "interceptor == null"))
        return this as R
    }

    fun addNetworkInterceptor(interceptor: Interceptor): R {
        networkInterceptors.add(Utils.checkNotNull(interceptor, "interceptor == null"))
        return this as R
    }

    fun addCookie(name: String?, value: String?): R {
        val builder = Cookie.Builder()
        name?.let { value?.let { it1 -> builder.name(it).value(it1).domain(httpUrl!!.host).build() } }?.let { cookies.add(it) }
        return this as R
    }

    fun addCookie(cookie: Cookie?): R {
        cookies.add(cookie)
        return this as R
    }

    fun addCookies(cookies: List<Cookie?>?): R {
        this.cookies.addAll(cookies!!)
        return this as R
    }

    /**
     * 设置Converter.Factory,默认GsonConverterFactory.create()
     */
    fun addConverterFactory(factory: Converter.Factory): R {
        converterFactories.add(factory)
        return this as R
    }

    /**
     * 设置CallAdapter.Factory,默认RxJavaCallAdapterFactory.create()
     */
    fun addCallAdapterFactory(factory: CallAdapter.Factory): R {
        adapterFactories.add(factory)
        return this as R
    }

    /**
     * 设置代理
     */
    fun okproxy(proxy: Proxy?): R {
        this.proxy = proxy
        return this as R
    }

    /**
     * 设置缓存的转换器
     */
    fun cacheDiskConverter(converter: IDiskConverter): R {
        diskConverter = Utils.checkNotNull(converter, "converter == null")
        return this as R
    }

    /**
     * https的全局访问规则
     */
    fun hostnameVerifier(hostnameVerifier: HostnameVerifier?): R {
        this.hostnameVerifier = hostnameVerifier
        return this as R
    }

    /**
     * https的全局自签名证书
     */
    fun certificates(vararg certificates: InputStream?): R {
        sslParams = getSslSocketFactory(null, null, certificates)
        return this as R
    }

    /**
     * https双向认证证书
     */
    fun certificates(
        bksFile: InputStream?,
        password: String?,
        vararg certificates: InputStream?
    ): R {
        sslParams = getSslSocketFactory(bksFile, password, certificates)
        return this as R
    }

    /**
     * 添加头信息
     */
    fun headers(headers: HttpHeaders?): R {
        this.headers.put(headers)
        return this as R
    }

    /**
     * 添加头信息
     */
    fun headers(key: String?, value: String?): R {
        headers.put(key, value)
        return this as R
    }

    /**
     * 移除头信息
     */
    fun removeHeader(key: String?): R {
        headers.remove(key!!)
        return this as R
    }

    /**
     * 移除所有头信息
     */
    fun removeAllHeaders(): R {
        headers.clear()
        return this as R
    }

    /**
     * 设置参数
     */
    fun params(params: HttpParams?): R {
        this.params.put(params)
        return this as R
    }

    fun params(key: String?, value: String?): R {
        params.put(key, value)
        return this as R
    }

    fun params(keyValues: Map<String?, String?>?): R {
        params.put(keyValues)
        return this as R
    }

    fun removeParam(key: String): R {
        params.remove(key)
        return this as R
    }

    fun removeAllParams(): R {
        params.clear()
        return this as R
    }

    fun sign(sign: Boolean): R {
        this.sign = sign
        return this as R
    }

    fun timeStamp(timeStamp: Boolean): R {
        this.timeStamp = timeStamp
        return this as R
    }

    fun accessToken(accessToken: Boolean): R {
        this.accessToken = accessToken
        return this as R
    }

    fun syncRequest(syncRequest: Boolean): R {
        isSyncRequest = syncRequest
        return this as R
    }

    /**
     * 移除缓存（key）
     */
    fun removeCache(key: String) {
        EasyHttp.rxCache.remove(key).compose(RxUtil.io_main())
            .subscribe({ HttpLog.i("removeCache success!!!") }) { throwable -> HttpLog.i("removeCache err!!!$throwable") }
    }

    /**
     * 根据当前的请求参数，生成对应的OkClient
     */
    private fun generateOkClient(): OkHttpClient.Builder {
        return if (readTimeOut <= 0 && writeTimeOut <= 0 && connectTimeout <= 0 && sslParams == null && cookies.size == 0 && hostnameVerifier == null && proxy == null && headers.isEmpty) {
            val builder: OkHttpClient.Builder = EasyHttp.getOkHttpClientBuilder()
            for (interceptor in builder.interceptors()) {
                if (interceptor is BaseDynamicInterceptor<*>) {
                    interceptor.sign(sign)!!
                        .timeStamp(timeStamp)!!.accessToken(accessToken)
                }
            }
            builder
        } else {
            val newClientBuilder: OkHttpClient.Builder = EasyHttp.okHttpClient.newBuilder()
            if (readTimeOut > 0) newClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS)
            if (writeTimeOut > 0) newClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS)
            if (connectTimeout > 0) newClientBuilder.connectTimeout(
                connectTimeout,
                TimeUnit.MILLISECONDS
            )
            hostnameVerifier?.let { newClientBuilder.hostnameVerifier(it) }
//            if (hostnameVerifier != null) newClientBuilder.hostnameVerifier(hostnameVerifier)
            if (sslParams != null) newClientBuilder.sslSocketFactory(
                sslParams!!.sSLSocketFactory!!,
                sslParams!!.trustManager!!
            )
            if (proxy != null) newClientBuilder.proxy(proxy)
            if (cookies.size > 0) EasyHttp.getCookieJar()?.addCookies(cookies as List<Cookie>)

            //添加头  头添加放在最前面方便其他拦截器可能会用到
            newClientBuilder.addInterceptor(HeadersInterceptor(headers))
            for (interceptor in interceptors) {
                if (interceptor is BaseDynamicInterceptor<*>) {
                    interceptor.sign(sign)!!
                        .timeStamp(timeStamp)!!.accessToken(accessToken)
                }
                newClientBuilder.addInterceptor(interceptor)
            }
            for (interceptor in newClientBuilder.interceptors()) {
                if (interceptor is BaseDynamicInterceptor<*>) {
                    interceptor.sign(sign)!!
                        .timeStamp(timeStamp)!!.accessToken(accessToken)
                }
            }
            if (networkInterceptors.size > 0) {
                for (interceptor in networkInterceptors) {
                    newClientBuilder.addNetworkInterceptor(interceptor)
                }
            }
            newClientBuilder
        }
    }

    /**
     * 根据当前的请求参数，生成对应的Retrofit
     */
    private fun generateRetrofit(): Retrofit.Builder {
        return if (converterFactories.isEmpty() && adapterFactories.isEmpty()) {
            val builder = EasyHttp.getRetrofitBuilder()
            if (!TextUtils.isEmpty(baseUrl)) {
                builder.baseUrl(baseUrl)
            }
            builder
        } else {
            val retrofitBuilder = Retrofit.Builder()
            if (!TextUtils.isEmpty(baseUrl)) retrofitBuilder.baseUrl(baseUrl)
            if (!converterFactories.isEmpty()) {
                for (converterFactory in converterFactories) {
                    retrofitBuilder.addConverterFactory(converterFactory)
                }
            } else {
                //获取全局的对象重新设置
                val newBuilder = EasyHttp.getRetrofitBuilder()
                if (!TextUtils.isEmpty(baseUrl)) {
                    newBuilder.baseUrl(baseUrl)
                }
                val listConverterFactory = newBuilder.build().converterFactories()
                for (factory in listConverterFactory) {
                    retrofitBuilder.addConverterFactory(factory)
                }
            }
            if (!adapterFactories.isEmpty()) {
                for (adapterFactory in adapterFactories) {
                    retrofitBuilder.addCallAdapterFactory(adapterFactory)
                }
            } else {
                //获取全局的对象重新设置
                val newBuilder = EasyHttp.getRetrofitBuilder()
                val listAdapterFactory = newBuilder.baseUrl(baseUrl).build().callAdapterFactories()
                for (factory in listAdapterFactory) {
                    retrofitBuilder.addCallAdapterFactory(factory)
                }
            }
            retrofitBuilder
        }
    }

    /**
     * 根据当前的请求参数，生成对应的RxCache和Cache
     */
    private fun generateRxCache(): RxCache.Builder {
        val rxCacheBuilder = EasyHttp.getRxCacheBuilder()
        when (cacheMode) {
            CacheMode.NO_CACHE -> {
                val NOCACHEINTERCEPTOR = NoCacheInterceptor()
                interceptors.add(NOCACHEINTERCEPTOR)
                networkInterceptors.add(NOCACHEINTERCEPTOR)
            }
            CacheMode.DEFAULT -> {
                if (cache == null) {
                    var cacheDirectory = EasyHttp.getCacheDirectory()
                    if (cacheDirectory == null) {
                        cacheDirectory = File(EasyHttp.context?.cacheDir, "okhttp-cache")
                    } else {
                        if (cacheDirectory.isDirectory && !cacheDirectory.exists()) {
                            cacheDirectory.mkdirs()
                        }
                    }
                    cache = Cache(
                        cacheDirectory,
                        Math.max((5 * 1024 * 1024).toLong(), EasyHttp.getCacheMaxSize())
                    )
                }
                val cacheControlValue = String.format("max-age=%d", Math.max(-1, cacheTime))
                val REWRITE_CACHE_CONTROL_INTERCEPTOR =
                    CacheInterceptor(EasyHttp.context, cacheControlValue)
                val REWRITE_CACHE_CONTROL_INTERCEPTOR_OFFLINE =
                    CacheInterceptorOffline(EasyHttp.context, cacheControlValue)
                networkInterceptors.add(REWRITE_CACHE_CONTROL_INTERCEPTOR)
                networkInterceptors.add(REWRITE_CACHE_CONTROL_INTERCEPTOR_OFFLINE)
                interceptors.add(REWRITE_CACHE_CONTROL_INTERCEPTOR_OFFLINE)
            }
            CacheMode.FIRSTREMOTE, CacheMode.FIRSTCACHE, CacheMode.ONLYREMOTE, CacheMode.ONLYCACHE, CacheMode.CACHEANDREMOTE, CacheMode.CACHEANDREMOTEDISTINCT -> {
                interceptors.add(NoCacheInterceptor())
                return if (diskConverter == null) {
                    rxCacheBuilder.cachekey(
                        Utils.checkNotNull(
                            cacheKey,
                            "cacheKey == null"
                        )
                    )
                        .cacheTime(cacheTime)
                    rxCacheBuilder
                } else {
                    val cacheBuilder = EasyHttp.rxCache.newBuilder()
                    cacheBuilder.diskConverter(diskConverter)
                        .cachekey(
                            Utils.checkNotNull(
                                cacheKey,
                                "cacheKey == null"
                            )
                        )
                        .cacheTime(cacheTime)
                    cacheBuilder
                }
            }
        }
        return rxCacheBuilder
    }

    protected open fun build(): R {
        val rxCacheBuilder = generateRxCache()
        val okHttpClientBuilder: OkHttpClient.Builder = generateOkClient()
        if (cacheMode == CacheMode.DEFAULT) { //okhttp缓存
            okHttpClientBuilder.cache(cache)
        }
        val retrofitBuilder = generateRetrofit()
        okHttpClient = okHttpClientBuilder.build()
        retrofitBuilder.client(okHttpClient)
        retrofit = retrofitBuilder.build()
        rxCache = rxCacheBuilder.build()
        apiManager = retrofit?.create(ApiService::class.java)
        return this as R
    }

    protected abstract fun generateRequest(): Observable<ResponseBody?>?

    init {
        context = EasyHttp.context!!
        val config = EasyHttp.instance!!
        baseUrl = EasyHttp.getBaseUrl()
        /*if (!TextUtils.isEmpty(baseUrl)) {
            httpUrl = HttpUrl.parse(baseUrl)
        }*/
        baseUrl?.let {  httpUrl = it.toHttpUrlOrNull() }
        if (baseUrl == null && url != null && (url!!.startsWith("http://") || url!!.startsWith("https://"))) {
            httpUrl = url!!.toHttpUrlOrNull()
            baseUrl = httpUrl!!.toUrl().protocol + "://" + httpUrl!!.toUrl().host + "/"
        }
        cacheMode = EasyHttp.getCacheMode()!! //添加缓存模式
        cacheTime = EasyHttp.getCacheTime()!! //缓存时间
        retryCount = EasyHttp.getRetryCount()!! //超时重试次数
        retryDelay = EasyHttp.getRetryDelay()!! //超时重试延时
        retryIncreaseDelay = EasyHttp.getRetryIncreaseDelay()!! //超时重试叠加延时
        //Okhttp  cache
        cache = EasyHttp.getHttpCache()
        //默认添加 Accept-Language
        val acceptLanguage = acceptLanguage
        if (!TextUtils.isEmpty(acceptLanguage)) headers(
            HttpHeaders.HEAD_KEY_ACCEPT_LANGUAGE,
            acceptLanguage
        )
        //默认添加 User-Agent
        val userAgent = userAgent
        if (!TextUtils.isEmpty(userAgent)) headers(HttpHeaders.HEAD_KEY_USER_AGENT, userAgent)
        //添加公共请求参数
        if (config.commonParams != null) params.put(config.commonParams)
        if (config.commonHeaders != null) headers.put(config.commonHeaders)
    }
}